/* Mobiling functions 
 *
 *
*/
#include <stdlib.h>
#include <GL/glew.h>
#include <GL/glu.h>
#include <GL/glut.h>
#include <stdio.h>

#include "defs.h"
#include "hw4.h"
#include "mobile.h"

float colors[MAX_PLATES][3] = { {1.0, 1.0, 1.0}, 	/* White */
							  {1.0, 0, 0}, 		/* Red */
							  {0, 1.0, 0}, 		/* green */
							  {0, 0, 1.0}, 		/* Blue */
							  {1.0, 1.0, 0},	/* Yellow */
							  {0, 1.0, 1.0}, 	/* Purple */
							  {204/255.0, 1.0, 0},
							  {51/255.0, 0, 51/255.0}, 
							  {0,153/255.0,0}, 
							  {51/255.0,104/255.0,204/255.0},
							  {153/255.0,51/255.0,51/255.0},
							  {153/255.0,153/255.0,1.0},
							  {1.0,102/255.0,102/255.0},
							  {0,102/255.0,1.0},
							  {1.0,153/255.0,0}};
							  
/* recursive to render the plates at a particular depth */
void draw_plate(int node, float tot_rot){
	SG* curr;
	int j, mid_x, mid_y;
	int depth;

	float t1x, t1y;
	float t2x, t2y;
	float t3x, t3y;
	float t4x, t4y;

	float sx, sy;
	float dx, dy;

	// recursion ends when there are no more nodes to render 
	if(node > max_node)
		return;
	curr = &scene[node];			// Point to the requested node 

	if ( node == 0 ) {
		depth = 0;
		sx = 0.;
		dx = 1.;

		if ( max_node > 1 ) {
			sy = 0;
			dy = .5;
		}

		else {
			///only node
			sy = 0.;
			dy = 1.;
		}
	}

	else if ( node < 3 ) {
		depth = 1;
		sx = ( node - 1 ) * .5;
		dx = .5;

		if ( max_node > 3 ) {
			sy = .5;
			dy = .25;
		}

		else {
			sy = .5;
			dy = .5;
		}
	}

	else if ( node < 7 ) {
		depth = 2;
		sx = ( node - 3 ) * .25;
		dx = .25;

		if ( max_node > 7 ) {
			sy = .75;
			dy = .125;
		}

		else {
			sy = .75;
			dy = .25;
		}
	}

	else if ( node < 15 ) {
		depth = 3;
		sx = ( node - 7 ) * .125;
		dx = .125;

		sy = .875;
		dy = .125;
	}

	else {
		depth = UNDEFINED;
		dx = 0;
		dy = 0;
	}

	
	t1x = sx; t1y = sy;
	t2x = sx; t2y = sy + dy;
	t3x = sx + dx; t3y = sy + dy;
	t4x = sx + dx; t4y = sy;

	/* prepare shader for texturing */
	if(use_shader){
		glUseProgram ( shaderProg [ 1 ] );

		GLint mapHandle = glGetUniformLocation ( shaderProg [ 1 ], "image" );
		glUniform1i ( mapHandle, 0 );

		GLint widthHandle = glGetUniformLocation ( shaderProg [ 1 ], "width" );
		glUniform1i ( widthHandle, WIDTH );

		GLint segHandle = glGetUniformLocation ( shaderProg [ 1 ], "segments" );
		glUniform1i ( segHandle, SEGMENTS );

		GLint n0xH = glGetUniformLocation ( shaderProg [ 1 ], "n0x" );
		glUniform1f ( n0xH, normal [ 0 ][ X ] );

		GLint n0yH = glGetUniformLocation ( shaderProg [ 1 ], "n0y" );
		glUniform1f ( n0yH, normal [ 0 ][ Y ] );

		GLint n1xH = glGetUniformLocation ( shaderProg [ 1 ], "n1x" );
		glUniform1f ( n1xH, normal [ 1 ][ X ] );

		GLint n1yH = glGetUniformLocation ( shaderProg [ 1 ], "n1y" );
		glUniform1f ( n1yH, normal [ 1 ][ Y ] );

		GLint n2xH = glGetUniformLocation ( shaderProg [ 1 ], "n2x" );
		glUniform1f ( n2xH, normal [ 2 ][ X ] );

		GLint n2yH = glGetUniformLocation ( shaderProg [ 1 ], "n2y" );
		glUniform1f ( n2yH, normal [ 2 ][ Y ] );

		GLint n3xH = glGetUniformLocation ( shaderProg [ 1 ], "n3x" );
		glUniform1f ( n3xH, normal [ 3 ][ X ] );

		GLint n3yH = glGetUniformLocation ( shaderProg [ 1 ], "n3y" );
		glUniform1f ( n3yH, normal [ 3 ][ Y ] );

		GLint n4xH = glGetUniformLocation ( shaderProg [ 1 ], "n4x" );
		glUniform1f ( n4xH, normal [ 4 ][ X ] );

		GLint n4yH = glGetUniformLocation ( shaderProg [ 1 ], "n4y" );
		glUniform1f ( n4yH, normal [ 4 ][ Y ] );

		GLint n5xH = glGetUniformLocation ( shaderProg [ 1 ], "n5x" );
		glUniform1f ( n5xH, normal [ 5 ][ X ] );

		GLint n5yH = glGetUniformLocation ( shaderProg [ 1 ], "n5y" );
		glUniform1f ( n5yH, normal [ 5 ][ Y ] );

		GLint n6xH = glGetUniformLocation ( shaderProg [ 1 ], "n6x" );
		glUniform1f ( n6xH, normal [ 6 ][ X ] );

		GLint n6yH = glGetUniformLocation ( shaderProg [ 1 ], "n6y" );
		glUniform1f ( n6yH, normal [ 6 ][ Y ] );

		GLint n7xH = glGetUniformLocation ( shaderProg [ 1 ], "n7x" );
		glUniform1f ( n7xH, normal [ 7 ][ X ] );

		GLint n7yH = glGetUniformLocation ( shaderProg [ 1 ], "n7y" );
		glUniform1f ( n7yH, normal [ 7 ][ Y ] );
	}


	/* transformation time */
	glPushMatrix();
		glColor3f(0.8, 0.8, 0.8);
		/* If this is the currently selected plate, then highlight it */
		if(selected_plate == node){
			glColor3fv(colors[0]);
		}
		
		/* update rotation angle */
		if(rotating){
			curr->curr_angle += curr->rot;
			if(curr->curr_angle > 360.0)
				curr->curr_angle = curr->curr_angle - 360.0;
		}
		/* calculate how much this plate is rotated from the viewer */
		tot_rot += curr->curr_angle;
		if(tot_rot > 360.0)
			tot_rot -= 360.0;

		// Translate the center of this plate to the origin and then rotate it
		mid_x = (curr->v[3].c[X]-curr->v[0].c[X])/2;
		glTranslatef(curr->v[0].c[X]+mid_x,0.0,0.0);
		glRotatef(curr->curr_angle, 0.0, 1.0, 0.0);

		// Draw the polygon for the plate
		glEnable ( GL_TEXTURE_2D );
		/* draw the front side texture */
		glBindTexture ( GL_TEXTURE_2D, image [ 0 ].tex );

		GLint uvcoord;
		GLfloat uv [ 2 ];
		if(use_shader){
			uvcoord = glGetAttribLocation ( shaderProg [ 1 ], "vertex_texture_coordinate" );
		}

		glBegin ( GL_QUADS );
	
			// Set the polygon vertices
			if(!use_shader)
				glTexCoord2f ( t1x, t1y );
			else {
				uv [ 0 ] = t1x; uv [ 1 ] = t1y;
				glVertexAttrib2fv ( uvcoord, uv );
			}
			glVertex3f ( mid_x,
				curr->v [ 2 ].c [ Y ],
				curr->v [ 2 ].c [ Z ] );

			if(!use_shader)
				glTexCoord2f ( t2x, t2y );
			else {
				uv [ 0 ] = t2x; uv [ 1 ] = t2y;
				glVertexAttrib2fv ( uvcoord, uv );
			}
			glVertex3f ( mid_x,
				curr->v [ 3 ].c [ Y ],
				curr->v [ 3 ].c [ Z ] );
			if(!use_shader)
				glTexCoord2f ( t3x, t3y );
			else { 
				uv [ 0 ] = t3x; uv [ 1 ] = t3y;
				glVertexAttrib2fv ( uvcoord, uv );
			}
			glVertex3f ( -mid_x,
				curr->v [ 0 ].c [ Y ],
				curr->v [ 0 ].c [ Z ] );
			if(!use_shader)
				glTexCoord2f ( t4x, t4y );
			else { 
				uv [ 0 ] = t4x; uv [ 1 ] = t4y;
				glVertexAttrib2fv ( uvcoord, uv );
			}
			glVertex3f ( -mid_x,
				curr->v [ 1 ].c [ Y ],
				curr->v [ 1 ].c [ Z ] );
				
		glEnd ();

		/* draw the back side texture */
		glBindTexture ( GL_TEXTURE_2D, image [ 1 ].tex );
		glBegin ( GL_QUADS );
	
			// Set the polygon vertices
			if(!use_shader)
				glTexCoord2f ( t1x, t1y );
			else {
				uv [ 0 ] = t1x; uv [ 1 ] = t1y;
				glVertexAttrib2fv ( uvcoord, uv );
			}
			glVertex3f ( -mid_x,
				curr->v [ 1 ].c [ Y ],
				curr->v [ 1 ].c [ Z ] );
			if(!use_shader)
				glTexCoord2f ( t2x, t2y );
			else {
				uv [ 0 ] = t2x; uv [ 1 ] = t2y;
				glVertexAttrib2fv ( uvcoord, uv );
			}
			glVertex3f ( -mid_x,
				curr->v [ 0 ].c [ Y ],
				curr->v [ 0 ].c [ Z ] );
			if(!use_shader)
				glTexCoord2f ( t3x, t3y );
			else {
					uv [ 0 ] = t3x; uv [ 1 ] = t3y;
				glVertexAttrib2fv ( uvcoord, uv );
			}
			glVertex3f ( mid_x,
				curr->v [ 3 ].c [ Y ],
				curr->v [ 3 ].c [ Z ] );
			if(!use_shader)
				glTexCoord2f ( t4x, t4y );
			else {
				uv [ 0 ] = t4x; uv [ 1 ] = t4y;
				glVertexAttrib2fv ( uvcoord, uv );
			}
			glVertex3f ( mid_x,
				curr->v [ 2 ].c [ Y ],
				curr->v [ 2 ].c [ Z ] );
							
		glEnd ();

		/* translate the matrix back to it's original starting place so 
		 * the other plates can be drawn correctly */
		glTranslatef(-(curr->v[0].c[X]+mid_x),0.0,0.0);

		/* figure out which child to draw first based on the rotation angle */
		if(tot_rot >0 && tot_rot <= 180){
			/* draw the right child first so it is in the back */
			if(node*2+1+1 <= max_node)
				draw_plate(node*2+2, tot_rot);
			/* Draw the left child last so it is in the front */
			if(node*2+1 <= max_node)
				draw_plate(node*2+1, tot_rot);
		} else {
			/* Recurse to the left child */
			if(node*2+1 <= max_node)
				draw_plate(node*2+1, tot_rot);
			/* Recurse to the right child */
			if(node*2+1+1 <= max_node)
				draw_plate(node*2+2, tot_rot);
		}
	glPopMatrix();
}

/* This function adds a row of plates at the bottom, splitting the bottom
 * row in half to create the new plates */
void add_row(){
	int old_max, oidx, nidx;
	int plate_h, plate_w;		// hieght and width of plates
	
	old_max = max_node;
	max_node = (max_node+1)*2;
	/* loop through the plates of the old bottom row and split its plates in half, 
	 * making the plates of the new bottom row. */
	oidx = (max_node/4);
	nidx = old_max+1;					// first node on new row
	for(; oidx<=old_max; oidx++){
		/* first set the vertices for the two children of this old node */
		/* calculate half sizes of the old plate */
		plate_h = (scene[oidx].v[1].c[Y] - scene[oidx].v[0].c[Y])/2-PLATE_SEP;
		plate_w = (scene[oidx].v[3].c[X] - scene[oidx].v[0].c[X])/2-PLATE_SEP;
		/* bottom left corner */
		scene[nidx].v[0].c[X] = scene[oidx].v[0].c[X];
		scene[nidx].v[0].c[Y] = scene[oidx].v[0].c[Y];
		scene[nidx].v[0].c[Z] = scene[oidx].v[0].c[Z];
		/* top left corner */
		scene[nidx].v[1].c[X] = scene[oidx].v[1].c[X];
		scene[nidx].v[1].c[Y] = scene[oidx].v[0].c[Y]+plate_h;
		scene[nidx].v[1].c[Z] = scene[oidx].v[1].c[Z];
		/* top right corner */
		scene[nidx].v[2].c[X] = scene[oidx].v[0].c[X]+plate_w;
		scene[nidx].v[2].c[Y] = scene[oidx].v[0].c[Y]+plate_h;
		scene[nidx].v[2].c[Z] = scene[oidx].v[2].c[Z];
		/* bottom right corner */
		scene[nidx].v[3].c[X] = scene[oidx].v[0].c[X]+plate_w;
		scene[nidx].v[3].c[Y] = scene[oidx].v[3].c[Y];
		scene[nidx].v[3].c[Z] = scene[oidx].v[3].c[Z];
		scene[nidx].curr_angle = scene[oidx].curr_angle;
		scene[nidx].rot = scene[oidx].rot;

		/* Second plate */
		nidx++;
		/* bottom left corner */
		scene[nidx].v[0].c[X] = scene[oidx].v[0].c[X] + plate_w + PLATE_SEP;
		scene[nidx].v[0].c[Y] = scene[oidx].v[0].c[Y];
		scene[nidx].v[0].c[Z] = scene[oidx].v[0].c[Z];
		/* top left corner */
		scene[nidx].v[1].c[X] = scene[oidx].v[1].c[X] + plate_w + PLATE_SEP;;
		scene[nidx].v[1].c[Y] = scene[oidx].v[0].c[Y] + plate_h;
		scene[nidx].v[1].c[Z] = scene[oidx].v[1].c[Z];
		/* top right corner */
		scene[nidx].v[2].c[X] = scene[oidx].v[2].c[X];
		scene[nidx].v[2].c[Y] = scene[oidx].v[0].c[Y] + plate_h;
		scene[nidx].v[2].c[Z] = scene[oidx].v[2].c[Z];
		/* bottom right corner */
		scene[nidx].v[3].c[X] = scene[oidx].v[3].c[X];
		scene[nidx].v[3].c[Y] = scene[oidx].v[3].c[Y];
		scene[nidx].v[3].c[Z] = scene[oidx].v[3].c[Z];
		scene[nidx].curr_angle = scene[oidx].curr_angle;
		scene[nidx].rot = scene[oidx].rot;

		/* Now fix the old plate to be half its size */
		/* bottom left corner */
		/* X coord stays the same */
		scene[oidx].v[0].c[Y] += plate_h+PLATE_SEP;
		/* Z coord stays the same */
		/* top left corner doesn't change */
		/* top right corner doesn't change */
		/* bottom right corner */
		/* X coord stays the same */
		scene[oidx].v[3].c[Y] += plate_h+PLATE_SEP;
		/* Z coord stays the same */

		nidx ++;  			// bump new plate index for next iteration
	}
	
}

/* This function removes the bottom row of plates, merging them with 
 * the plates above */
void remove_row(){
	int parent, child;
	int new_y;
	
	/* new bottom Y value for partent row of plates */
	new_y = scene[max_node].v[0].c[Y];
	/* loop through parent nodes and fix their Y values */
	child = max_node/2;				/* first child in the last row */
	for(parent = (child-1)/2; parent<child; parent++){
		scene[parent].v[0].c[Y] = new_y;
		scene[parent].v[3].c[Y] = new_y;
		print_plate(parent);
	}
		
	max_node = max_node/2 -1;		/* new max node is the end of the previous row */	
	printf("max_node %d\n", max_node);
}

void print_plate(int node){
	printf("plate %d: 0(%f,%f,%f) 1(%f,%f,%f) 2(%f,%f,%f) 3(%f,%f,%f)\n", node, scene[node].v[0].c[X], scene[node].v[0].c[Y], scene[node].v[0].c[Z],
			scene[node].v[1].c[X], scene[node].v[1].c[Y], scene[node].v[1].c[Z], scene[node].v[2].c[X], scene[node].v[2].c[Y], scene[node].v[2].c[Z],
			scene[node].v[3].c[X], scene[node].v[3].c[Y], scene[node].v[3].c[Z]);
}
